package common

import (
    "github.com/astaxie/beego"

    "encoding/json"
    "text/template"
    
    "reflect"
    "bytes"
    "time"
    "fmt"
)

/**
  该模块用于解析 通用查询语句参数形如
     将这样的json 解析成两个部分， 一个就是sql 语句中的where 部分， 2，就是对应的map 中存放的value

  *  第一步 先完成
*/

type AddToMap func(key string , value interface{}) string

func buildAddToMap(cacheMap map[string]interface{}) AddToMap {
    return func(key string, value interface{}) string {
        if _, ok := cacheMap[key]; !ok {
            cacheMap[key] = value
            return key
        } else {
            var newKey = fmt.Sprintf("%s%d",key, time.Now().Nanosecond())
            cacheMap[newKey] = value
            return newKey
        }
    }
}



func isset(v interface{}, name string) bool {
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Map {
        return false
    }
    contextInInterface := rv.Interface()
    mapContext := contextInInterface.(map[string]interface{})
    _, ok := mapContext[name]
    return ok
}

func issetMul(v interface{}, names ...string) bool {
    isSeted := true
    for _, name := range names {
        isSeted = isSeted && isset(v, name)
    }
    return isSeted
}

func ParaserCountForm(countFormJson []byte, conditions ...map[string]interface{}) (string, map[string]interface{}, error) {
    var countForm map[string]interface{}
    if err := json.Unmarshal([]byte(countFormJson), &countForm); err != nil {
        beego.Error(err)
        return "", nil, err //这个地方有问题,应该要继续返回错误
    }
    if countForm["offset"] != nil {
        delete(countForm, "offset")
    }
    if countForm["max"] != nil {
        delete(countForm, "max")
    }
    if countForm["order"] != nil {
        delete(countForm, "order")
    }
    addAndCondition(countForm, conditions)
    return generateSql(countForm)
}

func ParaserSearchForm(searchFormJson []byte, conditions ...map[string]interface{}) (string, map[string]interface{}, error) {
    var searchForm map[string]interface{}
    if err := json.Unmarshal(searchFormJson, &searchForm); err != nil {
        beego.Error(err)
        return "", nil, err //这个地方有问题,应该要继续返回错误
    }
    addAndCondition(searchForm, conditions)
    whereClause , searchParam, err := generateSql(searchForm)
    return whereClause, searchParam, err
}

func AddOtherCondition(column string, op string, value interface{}) (map[string]interface{}, error){
    otherCondition := map[string]interface{}{}
    otherCondition["field"] = column
    otherCondition["value"] = value
    otherCondition["op"] = op
    return otherCondition, nil
}

func addAndCondition(form map[string]interface{}, conditions []map[string]interface{}) error {
    query, existQuery := form["query"]
    if conditions != nil && len(conditions) > 0 {
        if !existQuery {
            var newAnd = map[string]interface{}{}
            newAnd["and"] = conditions
            form["query"] = newAnd
            return nil
        }
        queryCondition := query.(map[string]interface{})
        if and, exists := queryCondition["and"]; exists {
            andCondition := and.([]interface{})
            for _, condition := range conditions {
                andCondition = append(andCondition, condition)
            }
            queryCondition["and"] = andCondition
        } else {
            queryCondition["and"] = conditions
        }
        form["query"] = queryCondition
    } 
    return nil
}

// 生成sql语句, 并且能够将参数以map的形式返回.
// 现在这个版本还差校验
func generateSql(param map[string]interface{}) (string,map[string]interface{}, error) {
    myTemplate := `
    {{- if isset . "query" }} where {{ end -}}
    {{ define "and-template" }}
       ( {{ range $index, $value := .    }}
          {{ if gt $index 0 }} and {{ end -}}
          {{ if issetM $value "field" "value" "op" -}}
                {{ template "op_field" $value -}}
          {{ else if isset $value "and" -}}
              {{ template "and-template" $value.and -}}
          {{ else if isset $value "or" }}  {{ template "or-template" $value.or }}  {{ end -}}
        {{ end }} )     
    {{ end }}
    {{ define "or-template" }}
       ( {{ range $index, $value := .    }}
          {{ if gt $index 0 }} or {{ end -}}
          {{ if issetM $value "field" "value" "op" -}}
                {{ template "op_field" $value -}}
          {{ else if isset $value "and" -}}
                {{ template "and-template" $value.and -}}
          {{ else if isset $value "or" -}}
                {{ template "or-template" $value.or -}}
          {{ end -}}
        {{ end -}} )     
    {{ end -}}
    {{ define "op_field" -}}
      {{ if issetM . "field" "value" "op" }} {{ .field  }} {{ .op }} :{{ addToParams .field .value }}  {{ end -}}
    {{ end -}}
    {{/* 准备开始order */}}
    {{ if isset . "query" }}
        {{ range $key, $value := .query -}}
            {{ if eq $key "and" -}}
            {{ template "and-template" $value -}}
            {{ end -}}
        {{end -}}
    {{ end }}
    {{/* 只有存在query的时候才会去输出 */}}    
    {{- if isset . "order" }}  order by {{ .order.field }} {{ .order.order }} {{ end -}}
    {{ if isset . "max" }} limit {{ .max }} {{ end }} {{ if isset . "offset" }} offset {{.offset }} {{end}} 
      `
    var buffer bytes.Buffer
    var paramCached = make(map[string]interface{})
    var addToParams = buildAddToMap(paramCached)
    var (
        funcs = template.FuncMap{"isset": isset, "issetM": issetMul,"addToParams":addToParams}
    )

    if masterTmpl, err := template.New("master").Funcs(funcs).Parse(myTemplate); err != nil {
        fmt.Print(err.Error())
    } else if err := masterTmpl.Execute(&buffer, param); err != nil {
        fmt.Printf(err.Error())
    }
    return buffer.String(), paramCached, nil

}
